/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

/* ////////////////////////////////////////////////////////////////////
//
//  Mat basic operations: Copy, Set
//
// */

#include "precomp.hpp"

namespace cv {

template<typename T> static void
copyMask_(const Mat& srcmat, Mat& dstmat, const Mat& maskmat) {
	const uchar* mask = maskmat.data;
	size_t sstep = srcmat.step;
	size_t dstep = dstmat.step;
	size_t mstep = maskmat.step;
	Size size = getContinuousSize(srcmat, dstmat, maskmat);

	for ( int y = 0; y < size.height; y++, mask += mstep ) {
		const T* src = (const T*)(srcmat.data + sstep * y);
		T* dst = (T*)(dstmat.data + dstep * y);
		int x = 0;
		for ( ; x <= size.width - 4; x += 4 ) {
			if ( mask[x] ) {
				dst[x] = src[x];
			}
			if ( mask[x+1] ) {
				dst[x+1] = src[x+1];
			}
			if ( mask[x+2] ) {
				dst[x+2] = src[x+2];
			}
			if ( mask[x+3] ) {
				dst[x+3] = src[x+3];
			}
		}
		for ( ; x < size.width; x++ )
			if ( mask[x] ) {
				dst[x] = src[x];
			}
	}
}

template<typename T> static void
setMask_(const void* _scalar, Mat& dstmat, const Mat& maskmat) {
	T scalar = *(T*)_scalar;
	const uchar* mask = maskmat.data;
	size_t dstep = dstmat.step;
	size_t mstep = maskmat.step;
	Size size = dstmat.size();

	if ( dstmat.isContinuous() && maskmat.isContinuous() ) {
		size.width *= size.height;
		size.height = 1;
	}

	for ( int y = 0; y < size.height; y++, mask += mstep ) {
		T* dst = (T*)(dstmat.data + dstep * y);
		int x = 0;
		for ( ; x <= size.width - 4; x += 4 ) {
			if ( mask[x] ) {
				dst[x] = scalar;
			}
			if ( mask[x+1] ) {
				dst[x+1] = scalar;
			}
			if ( mask[x+2] ) {
				dst[x+2] = scalar;
			}
			if ( mask[x+3] ) {
				dst[x+3] = scalar;
			}
		}
		for ( ; x < size.width; x++ )
			if ( mask[x] ) {
				dst[x] = scalar;
			}
	}
}

typedef void (*SetMaskFunc)(const void* scalar, Mat& dst, const Mat& mask);

CopyMaskFunc g_copyMaskFuncTab[] = {
	0,
	copyMask_<uchar>, // 1
	copyMask_<ushort>, // 2
	copyMask_<Vec<uchar, 3> >, // 3
	copyMask_<int>, // 4
	0,
	copyMask_<Vec<ushort, 3> >, // 6
	0,
	copyMask_<int64>, // 8
	0, 0, 0,
	copyMask_<Vec<int, 3> >, // 12
	0, 0, 0,
	copyMask_<Vec<int64, 2> >, // 16
	0, 0, 0, 0, 0, 0, 0,
	copyMask_<Vec<int64, 3> >, // 24
	0, 0, 0, 0, 0, 0, 0,
	copyMask_<Vec<int64, 4> > // 32
};

static SetMaskFunc setMaskFuncTab[] = {
	0,
	setMask_<uchar>, // 1
	setMask_<ushort>, // 2
	setMask_<Vec<uchar, 3> >, // 3
	setMask_<int>, // 4
	0,
	setMask_<Vec<ushort, 3> >, // 6
	0,
	setMask_<int64>, // 8
	0, 0, 0,
	setMask_<Vec<int, 3> >, // 12
	0, 0, 0,
	setMask_<Vec<int64, 2> >, // 16
	0, 0, 0, 0, 0, 0, 0,
	setMask_<Vec<int64, 3> >, // 24
	0, 0, 0, 0, 0, 0, 0,
	setMask_<Vec<int64, 4> > // 32
};


/* dst = src */
void Mat::copyTo( Mat& dst ) const {
	if ( data == dst.data ) {
		return;
	}

	dst.create( rows, cols, type() );
	Size sz = size();
	const uchar* sptr = data;
	uchar* dptr = dst.data;

	sz.width *= (int)elemSize();
	if ( isContinuous() && dst.isContinuous() ) {
		sz.width *= sz.height;
		sz.height = 1;
	}

	for ( ; sz.height--; sptr += step, dptr += dst.step ) {
		memcpy( dptr, sptr, sz.width );
	}
}

void Mat::copyTo( Mat& dst, const Mat& mask ) const {
	if ( !mask.data ) {
		copyTo(dst);
		return;
	}

	uchar* data0 = dst.data;
	dst.create( size(), type() );
	if ( dst.data != data0 ) { // do not leave dst uninitialized
		dst = Scalar(0);
	}
	getCopyMaskFunc((int)elemSize())(*this, dst, mask);
}

Mat& Mat::operator = (const Scalar& s) {
	Size sz = size();
	uchar* dst = data;

	sz.width *= (int)elemSize();
	if ( isContinuous() ) {
		sz.width *= sz.height;
		sz.height = 1;
	}

	if ( s[0] == 0 && s[1] == 0 && s[2] == 0 && s[3] == 0 ) {
		for ( ; sz.height--; dst += step ) {
			memset( dst, 0, sz.width );
		}
	} else {
		int t = type(), esz1 = (int)elemSize1();
		double scalar[12];
		scalarToRawData(s, scalar, t, 12);
		int copy_len = 12 * esz1;
		uchar* dst_limit = dst + sz.width;

		if ( sz.height-- ) {
			while ( dst + copy_len <= dst_limit ) {
				memcpy( dst, scalar, copy_len );
				dst += copy_len;
			}
			memcpy( dst, scalar, dst_limit - dst );
		}

		if ( sz.height > 0 ) {
			dst = dst_limit - sz.width + step;
			for ( ; sz.height--; dst += step ) {
				memcpy( dst, data, sz.width );
			}
		}
	}
	return *this;
}

Mat& Mat::setTo(const Scalar& s, const Mat& mask) {
	if ( !mask.data ) {
		*this = s;
	} else {
		CV_Assert( channels() <= 4 );
		SetMaskFunc func = setMaskFuncTab[elemSize()];
		CV_Assert( func != 0 );
		double buf[4];
		scalarToRawData(s, buf, type(), 0);
		func(buf, *this, mask);
	}
	return *this;
}


template<typename T> static void
flipHoriz_( const Mat& srcmat, Mat& dstmat, bool flipv ) {
	uchar* dst0 = dstmat.data;
	size_t srcstep = srcmat.step;
	int dststep = (int)dstmat.step;
	Size size = srcmat.size();

	if ( flipv ) {
		dst0 += (size.height - 1) * dststep;
		dststep = -dststep;
	}

	for ( int y = 0; y < size.height; y++ ) {
		const T* src = (const T*)(srcmat.data + srcstep * y);
		T* dst = (T*)(dst0 + dststep * y);

		for ( int i = 0; i < (size.width + 1) / 2; i++ ) {
			T t0 = src[i], t1 = src[size.width - i - 1];
			dst[i] = t1; dst[size.width - i - 1] = t0;
		}
	}
}

typedef void (*FlipHorizFunc)( const Mat& src, Mat& dst, bool flipv );

static void
flipVert( const Mat& srcmat, Mat& dstmat ) {
	const uchar* src = srcmat.data;
	uchar* dst = dstmat.data;
	size_t srcstep = srcmat.step, dststep = dstmat.step;
	Size size = srcmat.size();
	const uchar* src1 = src + (size.height - 1) * srcstep;
	uchar* dst1 = dst + (size.height - 1) * dststep;
	size.width *= (int)srcmat.elemSize();

	for ( int y = 0; y < (size.height + 1) / 2; y++, src += srcstep, src1 -= srcstep,
			dst += dststep, dst1 -= dststep ) {
		int i = 0;
		if ( ((size_t)(src) | (size_t)(dst) | (size_t)src1 | (size_t)dst1) % sizeof(int) == 0 ) {
			for ( ; i <= size.width - 16; i += 16 ) {
				int t0 = ((int*)(src + i))[0];
				int t1 = ((int*)(src1 + i))[0];

				((int*)(dst + i))[0] = t1;
				((int*)(dst1 + i))[0] = t0;

				t0 = ((int*)(src + i))[1];
				t1 = ((int*)(src1 + i))[1];

				((int*)(dst + i))[1] = t1;
				((int*)(dst1 + i))[1] = t0;

				t0 = ((int*)(src + i))[2];
				t1 = ((int*)(src1 + i))[2];

				((int*)(dst + i))[2] = t1;
				((int*)(dst1 + i))[2] = t0;

				t0 = ((int*)(src + i))[3];
				t1 = ((int*)(src1 + i))[3];

				((int*)(dst + i))[3] = t1;
				((int*)(dst1 + i))[3] = t0;
			}

			for ( ; i <= size.width - 4; i += 4 ) {
				int t0 = ((int*)(src + i))[0];
				int t1 = ((int*)(src1 + i))[0];

				((int*)(dst + i))[0] = t1;
				((int*)(dst1 + i))[0] = t0;
			}
		}

		for ( ; i < size.width; i++ ) {
			uchar t0 = src[i];
			uchar t1 = src1[i];

			dst[i] = t1;
			dst1[i] = t0;
		}
	}
}

void flip( const Mat& src, Mat& dst, int flip_mode ) {
	static FlipHorizFunc tab[] = {
		0,
		flipHoriz_<uchar>, // 1
		flipHoriz_<ushort>, // 2
		flipHoriz_<Vec<uchar, 3> >, // 3
		flipHoriz_<int>, // 4
		0,
		flipHoriz_<Vec<ushort, 3> >, // 6
		0,
		flipHoriz_<int64>, // 8
		0, 0, 0,
		flipHoriz_<Vec<int, 3> >, // 12
		0, 0, 0,
		flipHoriz_<Vec<int64, 2> >, // 16
		0, 0, 0, 0, 0, 0, 0,
		flipHoriz_<Vec<int64, 3> >, // 24
		0, 0, 0, 0, 0, 0, 0,
		flipHoriz_<Vec<int64, 4> > // 32
	};

	dst.create( src.size(), src.type() );

	if ( flip_mode == 0 ) {
		flipVert( src, dst );
	} else {
		int esz = (int)src.elemSize();
		CV_Assert( esz <= 32 );
		FlipHorizFunc func = tab[esz];
		CV_Assert( func != 0 );

		if ( flip_mode > 0 ) {
			func( src, dst, false );
		} else if ( src.data != dst.data ) {
			func( src, dst, true );
		} else {
			func( dst, dst, false );
			flipVert( dst, dst );
		}
	}
}


void repeat(const Mat& src, int ny, int nx, Mat& dst) {
	dst.create(src.rows * ny, src.cols * nx, src.type());
	Size ssize = src.size(), dsize = dst.size();
	int esz = (int)src.elemSize();
	int x, y;
	ssize.width *= esz; dsize.width *= esz;

	for ( y = 0; y < ssize.height; y++ ) {
		for ( x = 0; x < dsize.width; x += ssize.width ) {
			memcpy( dst.data + y * dst.step + x, src.data + y * src.step, ssize.width );
		}
	}

	for ( ; y < dsize.height; y++ ) {
		memcpy( dst.data + y * dst.step, dst.data + (y - ssize.height)*dst.step, dsize.width );
	}
}

}

/* dst = src */
CV_IMPL void
cvCopy( const void* srcarr, void* dstarr, const void* maskarr ) {
	if ( CV_IS_SPARSE_MAT(srcarr) && CV_IS_SPARSE_MAT(dstarr)) {
		CV_Assert( maskarr == 0 );
		CvSparseMat* src1 = (CvSparseMat*)srcarr;
		CvSparseMat* dst1 = (CvSparseMat*)dstarr;
		CvSparseMatIterator iterator;
		CvSparseNode* node;

		dst1->dims = src1->dims;
		memcpy( dst1->size, src1->size, src1->dims * sizeof(src1->size[0]));
		dst1->valoffset = src1->valoffset;
		dst1->idxoffset = src1->idxoffset;
		cvClearSet( dst1->heap );

		if ( src1->heap->active_count >= dst1->hashsize * CV_SPARSE_HASH_RATIO ) {
			cvFree( &dst1->hashtable );
			dst1->hashsize = src1->hashsize;
			dst1->hashtable =
				(void**)cvAlloc( dst1->hashsize * sizeof(dst1->hashtable[0]));
		}

		memset( dst1->hashtable, 0, dst1->hashsize * sizeof(dst1->hashtable[0]));

		for ( node = cvInitSparseMatIterator( src1, &iterator );
				node != 0; node = cvGetNextSparseNode( &iterator )) {
			CvSparseNode* node_copy = (CvSparseNode*)cvSetNew( dst1->heap );
			int tabidx = node->hashval & (dst1->hashsize - 1);
			CV_MEMCPY_AUTO( node_copy, node, dst1->heap->elem_size );
			node_copy->next = (CvSparseNode*)dst1->hashtable[tabidx];
			dst1->hashtable[tabidx] = node_copy;
		}
		return;
	}
	cv::Mat src = cv::cvarrToMat(srcarr, false, true, 1), dst = cv::cvarrToMat(dstarr, false, true, 1);
	CV_Assert( src.depth() == dst.depth() && src.size() == dst.size() );

	int coi1 = 0, coi2 = 0;
	if ( CV_IS_IMAGE(srcarr) ) {
		coi1 = cvGetImageCOI((const IplImage*)srcarr);
	}
	if ( CV_IS_IMAGE(dstarr) ) {
		coi2 = cvGetImageCOI((const IplImage*)dstarr);
	}

	if ( coi1 || coi2 ) {
		CV_Assert( (coi1 != 0 || src.channels() == 1) &&
				   (coi2 != 0 || dst.channels() == 1) );

		int pair[] = { std::max(coi1 - 1, 0), std::max(coi2 - 1, 0) };
		cv::mixChannels( &src, 1, &dst, 1, pair, 1 );
		return;
	} else {
		CV_Assert( src.channels() == dst.channels() );
	}

	if ( !maskarr ) {
		src.copyTo(dst);
	} else {
		src.copyTo(dst, cv::cvarrToMat(maskarr));
	}
}

CV_IMPL void
cvSet( void* arr, CvScalar value, const void* maskarr ) {
	cv::Mat m = cv::cvarrToMat(arr);
	if ( !maskarr ) {
		m = value;
	} else {
		m.setTo(value, cv::cvarrToMat(maskarr));
	}
}

CV_IMPL void
cvSetZero( CvArr* arr ) {
	if ( CV_IS_SPARSE_MAT(arr) ) {
		CvSparseMat* mat1 = (CvSparseMat*)arr;
		cvClearSet( mat1->heap );
		if ( mat1->hashtable ) {
			memset( mat1->hashtable, 0, mat1->hashsize * sizeof(mat1->hashtable[0]));
		}
		return;
	}
	cv::Mat m = cv::cvarrToMat(arr);
	m = cv::Scalar(0);
}

CV_IMPL void
cvFlip( const CvArr* srcarr, CvArr* dstarr, int flip_mode ) {
	cv::Mat src = cv::cvarrToMat(srcarr);
	cv::Mat dst;

	if (!dstarr) {
		dst = src;
	} else {
		dst = cv::cvarrToMat(dstarr);
	}

	CV_Assert( src.type() == dst.type() && src.size() == dst.size() );
	cv::flip( src, dst, flip_mode );
}

CV_IMPL void
cvRepeat( const CvArr* srcarr, CvArr* dstarr ) {
	cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr);
	CV_Assert( src.type() == dst.type() &&
			   dst.rows % src.rows == 0 && dst.cols % src.cols == 0 );
	cv::repeat(src, dst.rows / src.rows, dst.cols / src.cols, dst);
}

/* End of file. */
